from pandac.PandaModules import *
from direct.actor.Actor import Actor # for animated models
from direct.task import Task    # for update functions
from panda3d.core import CollisionHandlerQueue,CollisionRay,BitMask32 
import math
import random
import cfg
from turret import Turret

TANK_SCALE = 1
TANK_SPEED = 10
TANK_TURN_SPEED = 70
TANK_START_X = 50
TANK_START_Y = 0
TANK_START_DIRECTION = 180
TANK_TURN_ERROR = 5
TANK_HEALTH = 100

LASER_SCALE = 1
LASER_SPEED = 30
LASER_TURN_SPEED = 110
LASER_START_X = -50
LASER_START_Y = 0
LASER_START_DIRECTION = 0
LASER_TURN_ERROR = 5
LASER_HEALTH = 50

ACID_SCALE = 1
ACID_SPEED = 20
ACID_TURN_SPEED = 80
ACID_START_X = 50
ACID_START_Y = -50
ACID_START_DIRECTION = 270
ACID_TURN_ERROR = 5
ACID_RANGE = 15
ACID_HEALTH = 75

class Enemy(object):
    def __init__(self):
        self.prevtime = 0
        self.traveled = 0
        self.cooldown = 1
        self.cooldowncounter = 0
        self.range = 8000
        self.type = "acid"
        cfg.enemy = self
        
    def distance(self, x, y):
        return math.sqrt(math.pow(x, 2) + math.pow(y, 2))

    def shoot(self, task):
        self.cooldowncounter += globalClock.getDt()
        if self.model and self.cooldowncounter > self.cooldown and self.model.getDistance(cfg.player.model) < self.range:
            self.cooldowncounter = 0
            self.turret.shoot(self.type)
            #print "shooting"
        return task.again
        
    def setupCollisions(self):
        # self.gRay = CollisionSegment(0, -20, 20, 0, -20, -500)
        self.gCol = CollisionNode("enemyRay")
        self.gCol.addSolid(self.gRay)
        self.gCol.setFromCollideMask(BitMask32.bit(0))
        self.gCol.setIntoCollideMask(BitMask32.allOff())
        self.gColNp = self.model.attachNewNode(self.gCol)
        self.gHandler = CollisionHandlerQueue()
        
        #self.gColNp.show()
        
        base.cTrav.addCollider(self.gColNp, self.gHandler)

        
    def collisionStuff(self):
        base.cTrav.traverse(render)
        
        entries = []
        for i in range(self.gHandler.getNumEntries()):
            entry = self.gHandler.getEntry(i)
            entries.append(entry)
            
        entries.sort(lambda a, b: cmp(b.getSurfacePoint(render).getZ(), a.getSurfacePoint(render).getZ()))
        
        if (len(entries) > 0) and (entries[0].getIntoNode().getName() == "collisionPlane"):
            z = entries[0].getSurfacePoint(render).getZ()
            self.model.setZ(z + 12)
        
class TankEnemy(Enemy):
    def __init__(self):
        Enemy.__init__(self)
        self.model = loader.loadModel("../models/tank_enemy.egg")
        self.model.setTexture(loader.loadTexture("../textures/tank_color.jpg"))
        ts = TextureStage('ts')
        ts.setMode(TextureStage.MModulateGlow)
        self.model.setTexture(ts, loader.loadTexture("../textures/tank_glow_temp2.jpg"))
        self.model.reparentTo(render)
        self.model.setScale(TANK_SCALE)
        self.lastPos = TANK_START_X, TANK_START_Y
        self.model.setPos(TANK_START_X, TANK_START_Y, 5)
        self.turn_speed = TANK_TURN_SPEED
        self.speed = TANK_SPEED
        self.direction = TANK_START_DIRECTION
        self.name = "Destructor"
        self.type = "plasma"
        self.cooldown = 2
        self.health = TANK_HEALTH
        self.turret = Turret("tankenemy", self.type, cfg.player.model)
        self.turret.model.reparentTo(self.model)
        self.turret.model.setPos(0,10,10)
        self.path_index = 0
        self.gridPath = self.initGridPath()
        # self.setupCollisions()
        taskMgr.add(self.shoot, "enemy attempting to shoot")
        taskMgr.add(self.move, "enemyMoveTask")
        
    def setupCollisions(self):
        self.gRay = CollisionSegment(0, -15, 20, 0, -15, -500)
        Enemy.setupCollisions(self)
        cTankSphere = CollisionSphere((0, 0, 4), 7)
        cTankNode = CollisionNode("tank")
        cTankNode.addSolid(cTankSphere)
        cTankNodePath = self.model.attachNewNode(cTankNode)
        #cTankNodePath.show()
    
    def initGridPath(self):
        """generates the random path for the enemy"""
        grid = []
        gridPath = [(50, 50)]
        
        for i in range(-9, 10):
            for j in range(-9, 10):
                grid.append((i*10, j*10))
                
        for i in range(len(grid)):
            randIndex = random.randint(0, len(grid) - 1)
            gridPath.append(grid[randIndex])
        
        return gridPath

    def move(self, task):
        if self.model:
            elapsed = task.time - self.prevtime
            dist = self.speed * elapsed

            heading = self.direction % 360

            dx = dist * math.sin(math.radians(heading))
            dy = dist * -math.cos(math.radians(heading))

            self.model.setPos(self.model.getX() + dx, self.model.getY() + dy, self.model.getZ())
            self.traveled += dist

            nextPos = self.gridPath[self.path_index % len(self.gridPath)]
            nextPos2 = self.gridPath[(self.path_index + 1) % len(self.gridPath)]
            vec1 = Vec2(self.lastPos[0] - nextPos[0], self.lastPos[1] - nextPos[1])
            vec2 = Vec2(nextPos[0] - nextPos2[0], nextPos[1] - nextPos2[1])

            vecx = Vec2(0, 1)

            length = self.distance(self.lastPos[0] - nextPos[0], self.lastPos[1] - nextPos[1])

            new_heading = (Vec2.signedAngleDeg(vec1, vec2) + Vec2.signedAngleDeg(vecx, vec1)) % 360

            if (self.traveled >= length):
                diff = self.turn(heading, new_heading, elapsed)

                if diff <= TANK_TURN_ERROR:
                    self.direction = new_heading
                    self.path_index += 1
                    self.lastPos = nextPos
                    self.traveled = 0

            self.prevtime = task.time
        
            self.collisionStuff()

        
        return Task.cont
        
    def turn(self, heading, new_heading, elapsed):
        # change in angle
        angle_difference = new_heading - heading
        
        if angle_difference < 0:
            angle_difference *= -1
            turn_direction = -1
        else:
            turn_direction = 1
        
        # we dont want to turn 270 degrees cw when we can turn 90 degrees ccw
        if angle_difference > 180:
            angle_difference = 360 - angle_difference
            turn_direction *= -1
        
        da = self.turn_speed * elapsed
        
        self.direction = (self.direction + da * turn_direction) % 360
        
        return angle_difference

    def gotHit(self, cEntry):
        if cEntry.getFromNodePath().getParent() in cfg.projectilelist:
            cfg.projectilelist.remove(cEntry.getFromNodePath().getParent())
            cEntry.getFromNodePath().getParent().remove()
            self.damaged()

    def damaged(self):
        snd = base.loader.loadSfx("../sounds/enemy_damage.wav")
        snd.setVolume(.2)
        snd.play()
        print "tank"
        print self.health
        self.hitType = cfg.player.firingmode
        if self.hitType == 0:
        # if self.health - 10 <= 0:
            # pass
            # #self.model.removeNode()
            # # cfg.enemy = LaserEnemy()
            # # cfg.enemy.setupCollisions()
        # else:
            self.health -= cfg.laserDmg
        if self.hitType == 1:
        # if self.health - 5 <= 0:
            # pass
            # #self.model.removeNode()
            # # cfg.enemy = LaserEnemy()
            # # cfg.enemy.setupCollisions()
        # else:
            self.health -= cfg.plasmaDmg
        if self.hitType == 2:
        # if self.health - 2 <= 0:
            # pass
            # #self.model.removeNode()
            # # cfg.enemy = LaserEnemy()
            # # cfg.enemy.setupCollisions()
        # else:
            self.health -= cfg.acidDmg


class LaserEnemy(Enemy):
    def __init__(self):
        Enemy.__init__(self)
        self.model = loader.loadModel("../models/laser_enemy.egg")
        self.model.setTexture(loader.loadTexture("../textures/laser_color.jpg"))
        self.model.reparentTo(render)
        self.model.setScale(LASER_SCALE)
        self.lastPos = LASER_START_X, LASER_START_Y
        self.model.setPos(LASER_START_X, LASER_START_Y, 5)
        self.turn_speed = LASER_TURN_SPEED
        self.speed = LASER_SPEED
        self.model.setH(LASER_START_DIRECTION)
        self.name = "Annoying"
        self.health = LASER_HEALTH
        self.path_index = 0
        self.type = "laser"
        self.cooldown = 1
        self.turret = Turret("laserenemy", self.type, cfg.player.model)
        self.turret.model.reparentTo(self.model)
        self.turret.model.setPos(0,10,10)
        self.gridPath = self.initGridPath()
        # self.setupCollisions()
        taskMgr.add(self.move, "enemyMoveTask")
        taskMgr.add(self.shoot, "enemy attempting to shoot")
        
    def setupCollisions(self):
        self.gRay = CollisionSegment(0, -5, 20, 0, -5, -500)
        Enemy.setupCollisions(self)
        cTankSphere = CollisionSphere((0, 0, 4), 7)
        cTankNode = CollisionNode("l_tank")
        cTankNode.addSolid(cTankSphere)
        cTankNodePath = self.model.attachNewNode(cTankNode)
        #cTankNodePath.show()
        
    def initGridPath(self):
        """generates the random path for the enemy"""
        grid = []
        gridPath = [(-50, -50)]
        
        for i in range(-7, 8):
            for j in range(-7, 8):
                grid.append((i*10, j*10))
                
        for i in range(len(grid)):
            randIndex = random.randint(0, len(grid) - 1)
            gridPath.append(grid[randIndex])
        
        return gridPath

    def move(self, task):
        if self.model:
            elapsed = task.time - self.prevtime
            dist = self.speed * elapsed

            heading = self.model.getH() % 360

            dx = dist * math.sin(math.radians(heading))
            dy = dist * -math.cos(math.radians(heading))

            self.model.setPos(self.model.getX() + dx, self.model.getY() + dy, self.model.getZ())
            self.traveled += dist

            nextPos = self.gridPath[self.path_index % len(self.gridPath)]
            nextPos2 = self.gridPath[(self.path_index + 1) % len(self.gridPath)]
            vec1 = Vec2(self.lastPos[0] - nextPos[0], self.lastPos[1] - nextPos[1])
            vec2 = Vec2(nextPos[0] - nextPos2[0], nextPos[1] - nextPos2[1])

            vecx = Vec2(0, 1)

            length = self.distance(self.lastPos[0] - nextPos[0], self.lastPos[1] - nextPos[1])

            new_heading = (Vec2.signedAngleDeg(vec1, vec2) + Vec2.signedAngleDeg(vecx, vec1)) % 360

            if (self.traveled >= length):
                diff = self.turn(heading, new_heading, elapsed)

                if diff <= LASER_TURN_ERROR:
                    self.model.setH(new_heading)
                    self.path_index += 1
                    self.lastPos = nextPos
                    self.traveled = 0

            self.prevtime = task.time

            self.collisionStuff()

        
        return Task.cont
        
    def turn(self, heading, new_heading, elapsed):
        # change in angle
        angle_difference = new_heading - heading
        
        if angle_difference < 0:
            angle_difference *= -1
            turn_direction = -1
        else:
            turn_direction = 1
        
        # we dont want to turn 270 degrees cw when we can turn 90 degrees ccw
        if angle_difference > 180:
            angle_difference = 360 - angle_difference
            turn_direction *= -1
        
        da = self.turn_speed * elapsed
        
        self.model.setH((self.model.getH() + da * turn_direction) % 360)
        
        return angle_difference

    def gotHit(self, cEntry):
        print "got hit"
        if cEntry.getFromNodePath().getParent() in cfg.projectilelist:
            cfg.projectilelist.remove(cEntry.getFromNodePath().getParent())
            cEntry.getFromNodePath().getParent().remove()
            self.damaged()

    def damaged(self):
        snd = base.loader.loadSfx("../sounds/enemy_damage.wav")
        snd.setVolume(.2)
        snd.play()
        print "laser"
        print self.health
        self.hitType = cfg.player.firingmode
        if self.hitType == 0:
        # if self.health - 10 <= 0:
            # pass
            # #self.model.removeNode()
            # # cfg.enemy = AcidEnemy()
            # # cfg.enemy.setupCollisions()
        # else:
            self.health -= cfg.laserDmg
        if self.hitType == 1:
        # if self.health - 5 <= 0:
            # pass
            # #self.model.removeNode()
            # # cfg.enemy = AcidEnemy()
            # # cfg.enemy.setupCollisions()
        # else:
            self.health -= cfg.plasmaDmg
        if self.hitType == 2:
        # if self.health - 2 <= 0:
            # pass
            # #self.model.removeNode()
            # # cfg.enemy = AcidEnemy()
            # # cfg.enemy.setupCollisions()
        # else:
            self.health -= cfg.acidDmg

class AcidEnemy(Enemy):
    def __init__(self):
        Enemy.__init__(self)
        self.model = loader.loadModel("../models/acid_enemy.egg")
        self.model.setTexture(loader.loadTexture("../textures/acid_color.jpg"))
        self.model.reparentTo(render)
        self.model.setScale(ACID_SCALE)
        self.lastPos = ACID_START_X, ACID_START_Y
        self.model.setPos(ACID_START_X, ACID_START_Y, 5)
        self.turn_speed = ACID_TURN_SPEED
        self.speed = ACID_SPEED
        self.direction = ACID_START_DIRECTION
        self.name = "Squidward"
        self.health = ACID_HEALTH
        self.range = 60
        self.telerange = 10
        self.turret = Turret("acidenemy", self.type, cfg.player.model)
        self.turret.model.reparentTo(self.model)
        self.turret.model.setPos(0,10,10)
        self.gridPath = self.initGridPath()
        self.path_index = 0
        self.type = "acid"
        self.cooldown = .1
        #self.setupCollisions()
        taskMgr.add(self.shoot, "enemy attempting to shoot")
        taskMgr.add(self.move, "enemyMoveTask")
        
    def initGridPath(self):
        """generates the random path for the enemy"""
        grid = []
        gridPath = []
        
        for i in range(-9, 10):
            for j in range(-9, 10):
                grid.append((i*10, j*10))
                
        for i in range(len(grid)):
            randIndex = random.randint(0, len(grid) - 1)
            gridPath.append(grid[randIndex])
        
        return gridPath
    
    
    def setupCollisions(self):
        self.gRay = CollisionSegment(0, -5, 20, 0, -5, -500)
        Enemy.setupCollisions(self)
        cTankSphere = CollisionSphere((0, 0, 4), 7)
        cTankNode = CollisionNode("a_tank")
        cTankNode.addSolid(cTankSphere)
        cTankNodePath = self.model.attachNewNode(cTankNode)
        #cTankNodePath.show()
    
    def move(self, task):

        elapsed = task.time - self.prevtime
        dist = self.speed * elapsed
        
        vec = Vec2(cfg.player.model.getX() - self.model.getX(), cfg.player.model.getY() - self.model.getY())
        vecx = Vec2(0, 1)
        
        heading = Vec2.signedAngleDeg(vecx, vec) % 360
        
        distance = self.distance(vec[0], vec[1])
        
        dx = dist * math.sin(heading)
        dy = dist * -math.cos(heading)
        
        self.model.setPos(self.model.getX() + dx, self.model.getY() + dy, self.model.getZ())
        
        if (distance <= self.telerange - 5):
            heading = (heading + 180) % 360
            self.model.setX(self.gridPath[self.path_index][0])
            self.model.setY(self.gridPath[self.path_index][1])
            self.path_index += 1
        
        self.collisionStuff()
        
        self.model.setH(self.turn_speed * elapsed + self.model.getH())
        
        self.prevtime = task.time

        return Task.cont


    def gotHit(self, cEntry):
        if cEntry.getFromNodePath().getParent() in cfg.projectilelist:
            cfg.projectilelist.remove(cEntry.getFromNodePath().getParent())
            cEntry.getFromNodePath().getParent().remove()
            self.damaged()

    def damaged(self):
        snd = base.loader.loadSfx("../sounds/enemy_damage.wav")
        snd.setVolume(.2)
        snd.play()
        print "tank"
        print self.health
        self.hitType = cfg.player.firingmode
        if self.hitType == 0:
            if self.health - 10 <= 0:
                #self.model.removeNode()
                self.health = 0
                self.model.hide()
                taskMgr.remove("enemy attempting to shoot")
            else:
                self.health -= cfg.laserDmg
        if self.hitType == 1:
            if self.health - 5 <= 0:
                #self.model.removeNode()
                self.health = 0
                self.model.hide()
                taskMgr.remove("enemy attempting to shoot")
            else:
                self.health -= cfg.plasmaDmg
        if self.hitType == 2:
            if self.health - 2 <= 0:
                #self.model.removeNode()
                self.health = 0
                self.model.hide()
                taskMgr.remove("enemy attempting to shoot")
            else:
                self.health -= cfg.acidDmg




